1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 12 /** 13 * Asset representation of a texture 14 */ 15 module hip.assets.texture; 16 import hip.asset; 17 import hip.error.handler; 18 import hip.hiprenderer.renderer; 19 import hip.math.rect; 20 import hip.assets.image; 21 public import hip.util.data_structures:Array2D; 22 public import hip.api.renderer.texture; 23 24 25 import renderer = hip.hiprenderer.texture; 26 import hip.util.reflection; 27 28 class HipTexture : HipAsset, IHipTexture 29 { 30 mixin(ForwardInterface!("textureImpl", IHipTexture)); 31 32 IImage img; 33 int width,height; 34 public IHipTexture textureImpl; 35 private bool successfullyLoaded; 36 public bool hasSuccessfullyLoaded(){return successfullyLoaded;} 37 38 public static HipTexture getPixelTexture() 39 { 40 __gshared HipTexture pixelTexture; 41 if(pixelTexture is null) 42 { 43 pixelTexture = new HipTexture(); 44 pixelTexture.img = cast(IImage)Image.getPixelImage; //Cast the immutable away, promise it is immutable 45 pixelTexture.textureImpl.load(pixelTexture.img); 46 } 47 return pixelTexture; 48 } 49 50 IHipTexture getBackendHandle(){return textureImpl.getBackendHandle();} 51 /** 52 * Initializes with the current renderer type 53 */ 54 protected this() 55 { 56 super("Texture"); 57 _typeID = assetTypeID!HipTexture; 58 textureImpl = HipRenderer.getTextureImplementation(); 59 } 60 61 62 this(in IImage image) 63 { 64 this(); 65 if(image !is null) 66 load(image); 67 } 68 69 import hip.util.string; 70 String toHipString() 71 { 72 return String("TEX[", width, "x",height,"] ", img.getSizeBytes, " bytes"); 73 } 74 alias load = IHipTexture.load; 75 76 77 protected bool loadImpl(in IImage img) 78 { 79 this.img = cast(IImage)img; 80 successfullyLoaded = textureImpl.load(img); 81 width = textureImpl.getWidth; 82 height = textureImpl.getHeight; 83 return successfullyLoaded; 84 } 85 86 override void onFinishLoading(){} 87 override void onDispose(){} 88 89 bool isReady(){return textureImpl !is null;} 90 int getWidth(){return width;} 91 int getHeight(){return height;} 92 93 } 94 95 96 97 class HipTextureRegion : HipAsset, IHipTextureRegion 98 { 99 public static const float[8] defaultVertices = [0,0, 1,0, 1,1, 0,1]; 100 IHipTexture texture; 101 public float u1, v1, u2, v2; 102 protected float[8] vertices; 103 protected float[8] verticesTransformed; 104 private bool flippedX, flippedY; 105 int regionWidth, regionHeight; 106 107 bool hasSuccessfullyLoaded(){return texture && texture.hasSuccessfullyLoaded;} 108 109 protected this() 110 { 111 super("TextureRegion"); 112 _typeID = assetTypeID!HipTextureRegion; 113 } 114 115 this(IHipTexture texture, float u1 = 0, float v1 = 0, float u2 = 1, float v2 = 1) 116 { 117 this(); 118 this.texture = texture; 119 setRegion(u1,v1,u2,v2); 120 } 121 this(IHipTexture texture, uint u1, uint v1, uint u2, uint v2) 122 { 123 this(); 124 this.texture = texture; 125 setRegion(texture.getWidth, texture.getHeight, u1, v1, u2, v2); 126 } 127 128 void setTexture(IHipTexture texture){this.texture = texture;} 129 const(IHipTexture) getTexture() const {return cast(const)texture;} 130 IHipTexture getTexture() {return texture;} 131 int getWidth() const {return regionWidth;} 132 int getHeight() const {return regionHeight;} 133 TextureCoordinatesQuad getRegion() const 134 { 135 return TextureCoordinatesQuad(u1, v1, u2, v2); 136 } 137 138 /** 139 * By passing the width and height values, you'll be able to crop useless frames 140 * Default spritesheet method that makes a spritesheet from the entire texture 141 */ 142 public static Array2D!IHipTextureRegion cropSpritesheet( 143 IHipTexture t, 144 uint frameWidth, uint frameHeight, 145 uint width = 0, uint height = 0, 146 uint offsetX = 0, uint offsetY = 0, 147 uint offsetXPerFrame = 0, uint offsetYPerFrame = 0) 148 { 149 if(width == 0) width = t.getWidth; 150 if(height == 0) height = t.getHeight; 151 152 uint lengthW = width/(frameWidth+offsetXPerFrame); 153 uint lengthH = height/(frameHeight+offsetYPerFrame); 154 155 Array2D!IHipTextureRegion ret = Array2D!IHipTextureRegion(lengthH, lengthW); 156 157 for(int i = 0, fh = 0; fh < height; i++, fh+= frameHeight+offsetXPerFrame) 158 for(int j = 0, fw = 0; fw < width; j++, fw+= frameWidth+offsetYPerFrame) 159 ret[i,j] = new HipTextureRegion(t, offsetX+fw , offsetY+fh, offsetX+fw+frameWidth, offsetY+fh+frameHeight); 160 161 return ret; 162 } 163 public static Array2D!IHipTextureRegion cropSpritesheetRowsAndColumns(IHipTexture t, uint rows, uint columns) 164 { 165 uint frameWidth = t.getWidth() / columns; 166 uint frameHeight = t.getHeight() / rows; 167 return cropSpritesheet(t,frameWidth,frameHeight, t.getWidth, t.getHeight, 0, 0, 0, 0); 168 } 169 170 171 alias setRegion = IHipTextureRegion.setRegion; 172 /** 173 * Defines a region for the texture in the following order: 174 * Top-left 175 * Top-Right 176 * Bot-Right 177 * Bot-Left 178 */ 179 public void setRegion(float u1, float v1, float u2, float v2) 180 { 181 this.u1 = u1; 182 this.u2 = u2; 183 this.v1 = v1; 184 this.v2 = v2; 185 //Check for round 186 float regWidth = (u2 - u1) * texture.getWidth; 187 float regHeight = (v2 - v1) * texture.getHeight; 188 regionWidth = cast(uint)(regWidth + 0.5) > cast(uint)regWidth ? cast(uint)(regWidth+0.5) : cast(uint)regWidth; 189 regionHeight = cast(uint)(regHeight + 0.5) > cast(uint)regHeight ? cast(uint)(regHeight+0.5) : cast(uint)regHeight; 190 191 //Top left 192 vertices[0] = u1; 193 vertices[1] = v1; 194 195 //Top right 196 vertices[2] = u2; 197 vertices[3] = v1; 198 199 //Bot right 200 vertices[4] = u2; 201 vertices[5] = v2; 202 203 //Bot left 204 vertices[6] = u1; 205 vertices[7] = v2; 206 207 if(flippedX) 208 { 209 flippedX = false; 210 setFlippedX(true); 211 } 212 if(flippedY) 213 { 214 flippedY = false; 215 setFlippedY(true); 216 } 217 } 218 219 HipTextureRegion clone() 220 { 221 return new HipTextureRegion(texture, u1, v1, u2, v2); 222 } 223 void setFlippedX(bool flip) 224 { 225 if(flip != flippedX) 226 { 227 flippedX = flip; 228 vertices[0] = flip ? u2 : u1; 229 vertices[2] = flip ? u1 : u2; 230 vertices[4] = flip ? u1 : u2; 231 vertices[6] = flip ? u2 : u1; 232 } 233 } 234 void setFlippedY(bool flip) 235 { 236 if(flip != flippedY) 237 { 238 flippedY = flip; 239 vertices[1] = flip ? v2 : v1; 240 vertices[3] = flip ? v2 : v1; 241 vertices[5] = flip ? v1 : v2; 242 vertices[7] = flip ? v1 : v2; 243 } 244 } 245 bool isFlippedX(){return flippedX;} 246 bool isFlippedY(){return flippedY;} 247 248 ref float[8] getVertices() 249 { 250 return vertices; 251 } 252 override void onFinishLoading(){} 253 override void onDispose(){} 254 bool isReady(){return texture !is null;} 255 256 }